﻿using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using IRepository;
using System;
using System.Collections.Generic;
using System.Data;
using MySql.Data.MySqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Model.Models;
using Model.Dto;

namespace Repository
{
    /// <summary>
    /// 基础仓储层实现类
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : class, new()
    {
        private gmdbContext _db;
        private readonly DbSet<TEntity> _dbSet;

        internal gmdbContext Db
        {
            get { return _db; }
            private set { _db = value; }
        }
        public BaseRepository(IgmdbContext mydbcontext)
        {
            this._db = mydbcontext as gmdbContext;
            this._dbSet = _db.Set<TEntity>();
        }

        #region 新增

        #region 单条数据新增
        /// <summary>
        /// 单条数据新增
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<bool> Add(TEntity model)
        {
            _db.Set<TEntity>().Add(model);
            return await _db.SaveChangesAsync() > 0;
        }
        #endregion

        #region 批量新增
        /// <summary>
        /// 批量新增
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<bool> AddRange(List<TEntity> model)
        {
            await _db.Set<TEntity>().AddRangeAsync(model);
            return await _db.SaveChangesAsync() == model.Count;
        }
        #endregion

        #endregion

        #region 删除
        #region 单条数据删除
        /// <summary>
        ///  单条数据删除
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<int> Delete(TEntity model)
        {
            _db.Set<TEntity>().Attach(model);
            _db.Set<TEntity>().Remove(model);
            return await _db.SaveChangesAsync();
        }
        #endregion

        #region 批量删除
        /// <summary>
        /// 批量删除
        /// </summary>
        /// <param name="sqlWhere"></param>
        /// <returns>返回受影响的行数</returns>
        public async Task<int> BatchDelete(Expression<Func<TEntity, bool>> sqlWhere)
        {
            // 查询要删除的数据
            List<TEntity> list = _db.Set<TEntity>().Where(sqlWhere).ToList();
            // 将要删除的数据 用删除方法添加到 EF 容器中
            list.ForEach(u =>
            {
                _db.Set<TEntity>().Attach(u);  //先附加到EF 容器
                _db.Set<TEntity>().Remove(u); //标识为删除状态
            });
            // 一次性生成sql语句 到数据库执行删除
            return await _db.SaveChangesAsync();
        }
        #endregion

        #endregion

        #region  查询 注：AsNoTracking无跟踪查询提高查询性能

        #region 查询当前实体所有数据
        /// <summary>
        /// 查询所有数据
        /// </summary>
        /// <returns></returns>
        public async Task<List<TEntity>> GetList()
        {
            return await _db.Set<TEntity>().AsNoTracking().ToListAsync();
        }
        #endregion    

        #region 根据查询条件查询集合
        /// <summary>
        /// 根据查询条件查询集合
        /// </summary>
        /// <param name="whereLambda"></param>
        /// <returns></returns>
        public async Task<List<TEntity>> GetByList(Expression<Func<TEntity, bool>> whereLambda)
        {
            return await _db.Set<TEntity>().Where(whereLambda).AsNoTracking().ToListAsync();
        }
        #endregion

        #region 根据查询条件查询第一个条数据
        /// <summary>
        /// 根据查询条件查询第一个条数据
        /// </summary>
        /// <param name="whereLambda"></param>
        /// <returns></returns>
        public async Task<TEntity> GetByIdFirstModel(Expression<Func<TEntity, bool>> whereLambda)
        {
            return await _db.Set<TEntity>().Where(whereLambda).AsNoTracking().FirstOrDefaultAsync();
        }
        #endregion

        #region 根据查询条件并单方向排序
        /// <summary>
        /// 根据查询条件并单方向排序
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="whereLambda">查询条件</param>
        /// <param name="orderLambda">排序字段</param>
        /// <param name="isAsc">排序方向</param>
        /// <returns></returns>
        public async Task<List<TEntity>> GetByList<TKey>(Expression<Func<TEntity, bool>> whereLambda, Expression<Func<TEntity, TKey>> orderLambda, bool isAsc = true)
        {
            if (isAsc)
            {
                return await _db.Set<TEntity>().Where(whereLambda).OrderBy(orderLambda).AsNoTracking().ToListAsync();
            }
            else
            {
                return await _db.Set<TEntity>().Where(whereLambda).OrderByDescending(orderLambda).AsNoTracking().ToListAsync();
            }
        }
        #endregion

        #region 根据查询条件并单方向排序取得前几条数据
        /// <summary>
        /// 根据查询条件并单方向排序取得前几条数据
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="top">取得第几条数据</param>
        /// <param name="whereLambda">查询条件</param>
        /// <param name="orderLambda">排序字段</param>
        /// <param name="isAsc">排序方向</param>
        /// <returns></returns>
        public async Task<List<TEntity>> GetByList<TKey>(int top, Expression<Func<TEntity, bool>> whereLambda, Expression<Func<TEntity, TKey>> orderLambda, bool isAsc = true)
        {
            if (isAsc)
            {
                return await _db.Set<TEntity>().Where(whereLambda).OrderBy(orderLambda).Take(top).AsNoTracking().ToListAsync();
            }
            else
            {
                return await _db.Set<TEntity>().Where(whereLambda).OrderByDescending(orderLambda).Take(top).AsNoTracking().ToListAsync();
            }
        }
        #endregion


        #region 根据条件排序查询  双排序 
        /// <summary>
        /// 根据条件排序查询  双排序
        /// </summary>
        /// <typeparam name="TKey1"></typeparam>
        /// <typeparam name="TKey2"></typeparam>
        /// <param name="whereLambda">查询条件</param>
        /// <param name="orderLambda1">排序字段</param>
        /// <param name="orderLambda2">排序字段</param>
        /// <param name="isAsc1">排序方向</param>
        /// <param name="isAsc2">排序方向</param>
        /// <returns></returns>
        public async Task<List<TEntity>> GetByList<TKey1, TKey2>(Expression<Func<TEntity, bool>> whereLambda, Expression<Func<TEntity, TKey1>> orderLambda1, Expression<Func<TEntity, TKey2>> orderLambda2, bool isAsc1 = true, bool isAsc2 = true)
        {
            if (isAsc1)
            {
                if (isAsc2)
                {
                    return await _db.Set<TEntity>().Where(whereLambda).OrderBy(orderLambda1).ThenBy(orderLambda2).AsNoTracking().ToListAsync();
                }
                else
                {
                    return await _db.Set<TEntity>().Where(whereLambda).OrderBy(orderLambda1).ThenByDescending(orderLambda2).AsNoTracking().ToListAsync();
                }
            }
            else
            {
                if (isAsc2)
                {
                    return await _db.Set<TEntity>().Where(whereLambda).OrderByDescending(orderLambda1).ThenBy(orderLambda2).AsNoTracking().ToListAsync();
                }
                else
                {
                    return await _db.Set<TEntity>().Where(whereLambda).OrderByDescending(orderLambda1).ThenByDescending(orderLambda2).AsNoTracking().ToListAsync();
                }
            }
        }
        #endregion

        #region 根据条件排序查询Top个数  双排序 
        /// <summary>
        /// 根据条件排序查询Top个数  双排序
        /// </summary>
        /// <typeparam name="TKey1"></typeparam>
        /// <typeparam name="TKey2"></typeparam>
        /// <param name="top">取得前几条数据</param>
        /// <param name="whereLambda">查询条件</param>
        /// <param name="orderLambda1">排序字段</param>
        /// <param name="orderLambda2">排序字段</param>
        /// <param name="isAsc1">排序方向</param>
        /// <param name="isAsc2">排序方向</param>
        /// <returns></returns>
        public async Task<List<TEntity>> GetByList<TKey1, TKey2>(int top, Expression<Func<TEntity, bool>> whereLambda, Expression<Func<TEntity, TKey1>> orderLambda1, Expression<Func<TEntity, TKey2>> orderLambda2, bool isAsc1 = true, bool isAsc2 = true)
        {
            if (isAsc1)
            {
                if (isAsc2)
                {
                    return await _db.Set<TEntity>().Where(whereLambda).OrderBy(orderLambda1).ThenBy(orderLambda2).Take(top).AsNoTracking().ToListAsync();
                }
                else
                {
                    return await _db.Set<TEntity>().Where(whereLambda).OrderBy(orderLambda1).ThenByDescending(orderLambda2).Take(top).AsNoTracking().ToListAsync();
                }
            }
            else
            {
                if (isAsc2)
                {
                    return await _db.Set<TEntity>().Where(whereLambda).OrderByDescending(orderLambda1).ThenBy(orderLambda2).Take(top).AsNoTracking().ToListAsync();
                }
                else
                {
                    return await _db.Set<TEntity>().Where(whereLambda).OrderByDescending(orderLambda1).ThenByDescending(orderLambda2).Take(top).AsNoTracking().ToListAsync();
                }
            }
        }
        #endregion

        #endregion

        #region 更新

        #region 更新实体
        /// <summary>
        /// 更新实体
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<int> Modify(TEntity model)
        {
            _db.Set<TEntity>().Update(model);
            return await _db.SaveChangesAsync();
        }
        #endregion

        #region 根据指定的属性更新实体
        /// <summary>
        /// 根据指定的属性更新实体
        /// </summary>
        /// <param name="model">修改实体</param>
        /// <param name="propertyName">可修改指定属性</param>
        /// <returns></returns>
        public async Task<int> Modify(TEntity model, params string[] propertyNames)
        {
            //3.1.1 将对象添加到EF中
            EntityEntry entry = _db.Entry<TEntity>(model);
            //3.1.2 先设置对象的包装状态为 Unchanged
            entry.State = EntityState.Unchanged;
            //3.1.3 循环被修改的属性名数组
            foreach (string propertyName in propertyNames)
            {
                //将每个被修改的属性的状态设置为已修改状态；这样在后面生成的修改语句时，就只为标识为已修改的属性更新
                entry.Property(propertyName).IsModified = true;
            }
            return await _db.SaveChangesAsync();
        }
        #endregion

        #region 根据指定的属性批量更新实体
        /// <summary>
        /// 根据指定的属性批量更新实体
        /// </summary>
        /// <param name="model">更新的数据</param>
        /// <param name="whereLambda">更新的条件</param>
        /// <param name="modifiedPropertyNames">要更新的字段</param>
        /// <returns></returns>
        public async Task<int> BatchModify(TEntity model, Expression<Func<TEntity, bool>> whereLambda, params string[] modifiedPropertyNames)
        {
            // 查询要修改的数据
            List<TEntity> listModifing = _db.Set<TEntity>().Where(whereLambda).ToList();
            // 获取实体类类型对象
            Type t = typeof(TEntity);
            // 获取实体类所有的公共属性
            List<PropertyInfo> propertyInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
            // 创建实体属性字典集合
            Dictionary<string, PropertyInfo> dicPropertys = new Dictionary<string, PropertyInfo>();
            // 将实体属性中要修改的属性名 添加到字典集合中  键：属性名  值：属性对象
            propertyInfos.ForEach(p =>
            {
                if (modifiedPropertyNames.Contains(p.Name))
                {
                    dicPropertys.Add(p.Name, p);
                }
            });
            // 循环要修改的属性名
            foreach (string propertyName in modifiedPropertyNames)
            {
                //判断要修改的属性名是否在实体类的属性集合中存在
                if (dicPropertys.ContainsKey(propertyName))
                {
                    //如果存在，则取出要修改的属性对象
                    PropertyInfo proInfo = dicPropertys[propertyName];
                    //取出要修改的值
                    object newValue = proInfo.GetValue(model, null);
                    //批量设置要修改对象的属性
                    foreach (TEntity item in listModifing)
                    {
                        //为要修改的对象的要修改的属性设置新的值
                        proInfo.SetValue(item, newValue, null);
                    }
                }
            }
            //一次性生成sql语句 到数据库执行
            return await _db.SaveChangesAsync();
        }
        #endregion

        #endregion


        #region 分页

        #region 6.0 分页查询 一定注意： Skip 之前一定要 OrderBy
        /// <summary>
        /// 分页查询 + List<TEntity> GetPagedList
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">每页多少条数据</param>
        /// <param name="whereLambda">条件 lambda表达式</param>
        /// <param name="orderBy">排序 lambda表达式</param>
        /// <returns></returns>
        public async Task<List<TEntity>> GetPagedList<TKey>(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> whereLambda, Expression<Func<TEntity, TKey>> orderByLambda, bool isAsc = true)
        {
            if (isAsc)
            {
                return await _db.Set<TEntity>().Where(whereLambda).OrderBy(orderByLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize).AsNoTracking().ToListAsync();
            }
            else
            {
                return await _db.Set<TEntity>().Where(whereLambda).OrderByDescending(orderByLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize).AsNoTracking().ToListAsync();
            }
        }
        #endregion

        #region 6.1分页查询 带输出
        /// <summary>
        /// 分页查询 带输出
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">每页多少条数据</param>
        /// <param name="rowCount">根据每页多少个算出总共分多少页</param>
        /// <param name="whereLambda">条件</param>
        /// <param name="orderBy">排序</param>
        /// <param name="isAsc">排序方向</param>
        /// <returns></returns>
        public async Task<PageDto<TEntity>> GetPagedList<TKey>(Expression<Func<TEntity, bool>> whereLambda, Expression<Func<TEntity, TKey>> orderByLambda, bool isAsc = true, int pageIndex = 1, int pageSize = 20)
        {
            // 数据数量
            var rowCount = await _db.Set<TEntity>().Where(whereLambda).CountAsync();
            //根据每页多少个算出总共分多少页
            int pageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(rowCount) / Convert.ToDecimal(pageSize)));

            List<TEntity> list = null;

            if (isAsc)
            {
                list = await _db.Set<TEntity>().OrderBy(orderByLambda).Where(whereLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize).AsNoTracking().ToListAsync();
            }
            else
            {
                list = await _db.Set<TEntity>().OrderByDescending(orderByLambda).Where(whereLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize).AsNoTracking().ToListAsync();
            }

            return new PageDto<TEntity>() { dataCount = rowCount, count = pageCount, page = pageIndex, PageSize = pageSize, data = list };
        }
        #endregion

        #endregion

        #region
        /// <summary>
        /// 回滚
        /// </summary>
        public void RollBackChanges()
        {
            var items = _db.ChangeTracker.Entries().ToList();
            items.ForEach(o => o.State = EntityState.Unchanged);
        }
        #endregion
    }
}
